Wonders.utils.LineLinkManager = Ext.extend(Ext.Component, {
	/**
	 * 连线存储key值的连接符
	 */
	linkSyntax : '_to_',
	/**
	 * 存储所有连线的连接点集合信息
	 */
	allLinkLine : new Ext.util.MixedCollection(),

	// override
	initComponent : function() {
		Wonders.utils.LineLinkManager.superclass.initComponent.call(this);
		this.initManager();
	},
	// private
	initManager : function() {
		this.addEvents(
				// 自定义验证事件，只有默认验证事件和自定义都通过后，才允许添加连线
				'validation');
		this.bind();
		this.container = Ext.DomHelper.append(document.body, {
			html : "&#160;",
			style : "position: relative;"
		}, true);
	},
	// private
	bind : function() {
		this.on("validation", this.validation);
	},
	// private just for test
	validation : function(linkMgr, srcElement, targetElement, color) {
		var reverseKey = targetElement.id + linkMgr.linkSyntax + srcElement.id;
		var isValidate1 = (typeof linkMgr.allLinkLine.get(reverseKey) == 'undefined');
		return isValidate1;
	},
	// private
	validateLink : function(srcElement, targetElement, color) {
		var isValidate1 = (typeof color != 'undefined');
		var key = srcElement.id + this.linkSyntax + targetElement.id;
		var isValidate2 = (typeof this.allLinkLine.get(key) == 'undefined');
		// 自定义验证 TODO 自己需要测试一下
		var isValidate3 = (this.fireEvent("validation", this, srcElement,
				targetElement, color) !== false);
		return isValidate1 && isValidate2 && isValidate3;
	},
	// public
	drawLink : function(srcElement, targetElement, color) {
		if (this.validateLink(srcElement, targetElement, color)) {
			var key = srcElement.id + this.linkSyntax + targetElement.id;
			this.allLinkLine.add(key, {
				source : srcElement,
				target : targetElement,
				color : color
			});
			// TODO 将节点加入到区域内。为后续expand&collapse判断做准备
			this.drawLineByElement(srcElement, targetElement, color);
		} else {
			alert('连线错误!');
		}
	},
	// public
	refreshLine : function() {
		this.container.dom.innerHTML = '';
		this.allLinkLine.each(function(item, index, length) {
			this.drawLineByElement(item.source, item.target, item.color);
		}, this);
	},
	drawLineByElement : function(srcElement, targetElement, color) {
		srcElement = this.getElementLocation(srcElement);
		targetElement = this.getElementLocation(targetElement);
		var xindent = (srcElement.getRight() - srcElement.getLeft()) / 2;
		var yindent = (srcElement.getBottom() - srcElement.getTop()) / 2;
		var sourceNodePoint = {
			x : srcElement.getRight() - xindent,
			y : srcElement.getBottom() - yindent
		};
		xindent = (targetElement.getRight() - targetElement.getLeft()) / 2;
		yindent = (targetElement.getBottom() - targetElement.getTop()) / 2;
		var targetNodePoint = {
			x : targetElement.getRight() - xindent,
			y : targetElement.getBottom() - yindent
		};

		this.drawLineByCoordination(sourceNodePoint.x, sourceNodePoint.y,
				targetNodePoint.x, targetNodePoint.y, color);
	},
	/**
	 * public 获取其父对象
	 */
	getElementLocation : function(childElement) {
		// default
		// var parentElement = childElement;
		// seek visible parent from this one.
		// var nodeElement = childElement.findParent('.x-tree-node', 4, true);
		if (!childElement.isVisible(true)) {
			var p = childElement.dom.parentNode;
			while (p && p.tagName.toLowerCase() != "body") {
				if (!Ext.fly(p, '_isVisible').isVisible()) {
					break;
				}
				p = p.parentNode;
			}
			// childElement = p.parentNode.firstChild.lastChild;
			// 获得可显示的节点图标的位置
			childElement = Ext.get(p.parentNode.firstChild.lastChild.lastChild);
		}
		return childElement;
	},
	// private
	drawLineByCoordination : function(x0, y0, x1, y1, color) {
		var rs = "";
		if (y0 == y1) { // 画横线
			if (x0 > x1) {
				var t = x0;
				x0 = x1;
				x1 = t
			}
			rs = "<span style='top:" + y0 + ";left:" + x0
					+ ";position:absolute;font-size:1px;background-color:"
					+ color + ";height:1; width:" + Math.abs(x1 - x0)
					+ "'></span>";
		} else if (x0 == x1) // 画竖线
		{
			if (y0 > y1) {
				var t = y0;
				y0 = y1;
				y1 = t
			}
			rs = "<span style='top:" + y0 + ";left:" + x0
					+ ";position:absolute;font-size:1px;background-color:"
					+ color + ";width:1;height:" + Math.abs(y1 - y0)
					+ "'></span>";
		} else {
			var lx = x1 - x0
			var ly = y1 - y0
			var l = Math.sqrt(lx * lx + ly * ly)
			rs = new Array();
			for (var i = 0; i < l; i += 1) {
				var p = i / l;
				// calculate the increment
				var px = parseInt(x0 + lx * p);
				var py = parseInt(y0 + ly * p);
				rs[rs.length] = "<span style='top:"
						+ py
						+ ";left:"
						+ px
						+ ";height:1;width:1;position:absolute;font-size:1px;background-color:"
						+ color + "'></span>";
			}
			rs = rs.join("");
		}

		this.container.dom.innerHTML += rs;

		return rs
	}
});
Ext.reg('lineLinkManager', Wonders.utils.LineLinkManager);

// override pervious/former version
Ext.tree.TreeDragZone.prototype.getDragData = function(e) {
	// TODO: need further study on Ext.dd.Registry later....
	var targetElement = Ext.get(e.getTarget());
	var isLineLink = targetElement.getAttributeNS('ext', 'lineDD');
	var handle = Ext.dd.Registry.getHandleFromEvent(e);
	if (handle) {
		handle.isLineLink = isLineLink;
	}
	return handle;
};
// override former version
Ext.tree.TreeDragZone.prototype.onInitDrag = function(e) {
	var data = this.dragData;
	this.tree.getSelectionModel().select(data.node);
	this.tree.eventModel.disable();
	this.proxy.update("");
	// add start
	// test
	Ext.getCmp('sample').body.dom.innerHTML = data.isLineLink;
	if (data.isLineLink) {
		this.proxy.ghost.dom.innerHTML = '<img src="http://localhost:8090/widget_center/struts/ext2/resources/images/default/tree/folder.gif"/>';// .appendChild(data.node.ui.lineDDNode.cloneNode(true));
	} else {
		data.node.ui.appendDDGhost(this.proxy.ghost.dom);
	}
	// add over
	this.tree.fireEvent("startdrag", this.tree, data.node, e);
};
// override former version
Ext.tree.TreeDropZone.prototype.completeDrop = function(de) {
	var ns = de.dropNode, p = de.point, t = de.target;
	// added start
	var isLineLink = de.data.isLineLink;
	// added over
	if (!(ns instanceof Array)) {
		ns = [ns];
	}
	var n;
	for (var i = 0, len = ns.length; i < len; i++) {
		n = ns[i];
		if (p == "above") {
			if (!isLineLink) {
				t.parentNode.insertBefore(n, t);
			}
		} else if (p == "below") {
			if (!isLineLink) {
				t.parentNode.insertBefore(n, t.nextSibling);
			}
		} else {
			if (!isLineLink) {
				t.appendChild(n);
			}
		}
		// added start
		if (isLineLink) {
			var lineLinkManager = Ext.getCmp('lineLinkManager');
			lineLinkManager.drawLink(Ext.get(n.ui.lineDDNode), Ext
					.get(t.ui.lineDDNode), "0000dd");
		}
		// added over
	}
	n.ui.focus();
	if (this.tree.hlDrop) {
		n.ui.highlight();
	}
	t.ui.endDrop();
	this.tree.fireEvent("nodedrop", de);
};
// override former version
Ext.tree.TreeNodeUI.prototype.initEvents = function() {
	this.node.on("move", this.onMove, this);

	if (this.node.disabled) {
		this.addClass("x-tree-node-disabled");
	}
	if (this.node.hidden) {
		this.hide();
	}
	var ot = this.node.getOwnerTree();
	var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
	// TODO
	if (dd && this.node.ownerTree.enableLink
			&& (!this.node.isRoot || ot.rootVisible)) {
		Ext.dd.Registry.register(this.lineDDNode, {
			node : this.node,
			// TODO
			handles : [this.lineDDNode],
			isHandle : false
		});
	}

	if (dd && (!this.node.isRoot || ot.rootVisible)) {
		Ext.dd.Registry.register(this.elNode, {
			node : this.node,
			handles : this.getDDHandles(),
			isHandle : false
		});
	}
};
// remain unchanged
Ext.tree.TreeNodeUI.prototype.getDDHandles = function() {
	return [this.iconNode, this.textNode, this.elNode];
};
// override former version
Ext.tree.TreeNodeUI.prototype.renderElements = function(n, a, targetNode,
		bulkRender) {
	// add some indent caching, this helps performance when rendering a large
	// tree
	this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';

	var cb = typeof a.checked == 'boolean';

	var href = a.href ? a.href : Ext.isGecko ? "" : "#";
	var buf = [
			'<li class="x-tree-node"><div ext:tree-node-id="',
			n.id,
			'" class="x-tree-node-el x-tree-node-leaf x-unselectable ',
			a.cls,
			'" unselectable="on">',
			'<span class="x-tree-node-indent">',
			this.indentMarkup,
			"</span>",
			'<img src="',
			this.emptyIcon,
			'" class="x-tree-ec-icon x-tree-elbow" />',
			'<img src="',
			a.icon || this.emptyIcon,
			'" class="x-tree-node-icon',
			(a.icon ? " x-tree-node-inline-icon" : ""),
			(a.iconCls ? " " + a.iconCls : ""),
			'" unselectable="on" />',
			cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked
					? 'checked="checked" />'
					: '/>')) : '',
			'<a hidefocus="on" class="x-tree-node-anchor" href="',
			href,
			'" tabIndex="1" ',
			a.hrefTarget ? ' target="' + a.hrefTarget + '"' : "",
			'>',
			'<span unselectable="on">',
			n.text,
			"</span>",
			// add extra element for line drag & drop
			'<img ext:lineDD=true src="http://localhost:8090/widget_center/struts/ext2/resources/images/default/tree/folder.gif" class="x-tree-node-icon',
			(a.icon ? " x-tree-node-inline-icon" : ""),
			(a.iconCls ? " " + a.iconCls : ""), '"  unselectable="on" />',
			"</a></div>",
			'<ul class="x-tree-node-ct" style="display:none;"></ul>', "</li>"]
			.join('');

	var nel;
	if (bulkRender !== true && n.nextSibling
			&& (nel = n.nextSibling.ui.getEl())) {
		this.wrap = Ext.DomHelper.insertHtml("beforeBegin", nel, buf);
	} else {
		this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf);
	}

	this.elNode = this.wrap.childNodes[0];
	this.ctNode = this.wrap.childNodes[1];
	var cs = this.elNode.childNodes;
	this.indentNode = cs[0];
	this.ecNode = cs[1];
	this.iconNode = cs[2];
	var index = 3;
	if (cb) {
		this.checkbox = cs[3];
		index++;
	}
	this.anchor = cs[index];
	this.textNode = cs[index].firstChild;
	// add extra element for line drag & drop
	this.lineDDNode = cs[index].childNodes[1];
};